using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XNOgre.Animation
{
    public class Skeleton
    {
        private Matrix[] boneTransforms, worldTransforms, skinTransforms;

        public Skeleton(List<Animation> animations, List<Matrix> bindPose, List<Matrix> inverseBindPose, List<int> skeletonHiearchy)
        {
            Animations = animations;
            BindPose = bindPose;
            InverseBindPose = inverseBindPose;
            SkeletonHiearchy = skeletonHiearchy;

        }
        private Skeleton()
        { }

        public void Initialize()
        {
            boneTransforms = new Matrix[BindPose.Count];
            worldTransforms = new Matrix[BindPose.Count];
            skinTransforms = new Matrix[BindPose.Count];

            for (int i = 0; i < Animations.Count; i++)
            {
                Animations[i].Parent = this;
            }
        }

        [ContentSerializer]
        public List<Animation> Animations { get; private set; }

        [ContentSerializer]
        public List<Matrix> BindPose { get; private set; }

        [ContentSerializer]
        public List<Matrix> InverseBindPose { get; private set; }

        [ContentSerializer]
        public List<int> SkeletonHiearchy { get; private set; }

        public Matrix[] BoneTransforms
        {
            get { return boneTransforms; }
        }
        public Matrix[] WorldTransforms
        {
            get { return worldTransforms; }
        }
        public Matrix[] SkinTransforms
        {
            get { return skinTransforms; }
        }
    }
    public class Keyframe
    {
        public Keyframe(int bone, TimeSpan time, Matrix transform)
        {
            Bone = bone;
            Time = time;
            Transform = transform;
        }
        private Keyframe()
        { }
        [ContentSerializer]
        public int Bone { get; private set; }

        [ContentSerializer]
        public TimeSpan Time { get; private set; }

        [ContentSerializer]
        public Matrix Transform { get; private set; }
    }
    public class Animation
    {
        private TimeSpan timePosition;
        public Animation(string name, TimeSpan duration, List<Keyframe> keyframes)
        {
            Name = name;
            Duration = duration;
            Keyframes = keyframes;

        }

        private Animation() { }

        [ContentSerializer]
        public string Name { get; private set; }

        [ContentSerializer]
        public TimeSpan Duration { get; private set; }

        [ContentSerializer]
        public List<Keyframe> Keyframes { get; private set; }

        [ContentSerializer(SharedResource=true)]
        public Skeleton Parent { get; set; }

        public TimeSpan TimePosition
        {
            get
            {
                return timePosition;
            }
            set
            {
                timePosition = value;
                SetCurrentKeyframe();
            }
        }

        public bool Loop { get; set; }

        public void AddTime(TimeSpan time)
        {
            timePosition += time;
            if (timePosition >= Duration)
            {
                if (!Loop)
                {
                    timePosition = Duration;
                }
                else
                {
                    timePosition = TimeSpan.Zero;
                }
            }

            SetCurrentKeyframe();
        }
        private void SetCurrentKeyframe()
        {
            if (Parent == null)
                return;

            //update bone transforms
            for (int i = 0; i < Keyframes.Count; i++)
            {
                if (timePosition >= Keyframes[i].Time)
                {
                    Parent.BoneTransforms[Keyframes[i].Bone] = Keyframes[i].Transform;
                    break;
                }
            }

            //update world transforms
            Parent.WorldTransforms[0] = Parent.BoneTransforms[0] * Matrix.Identity;
            for (int bone = 0; bone < Parent.WorldTransforms.Length; bone++)
            {
                int parentBone = Parent.SkeletonHiearchy[bone];
                Parent.WorldTransforms[bone] = Parent.BoneTransforms[bone] * Parent.WorldTransforms[parentBone];
            }

            //update skin transforms
            for (int bone = 0; bone < Parent.SkinTransforms.Length; bone++)
            {
                Parent.SkinTransforms[bone] = Parent.InverseBindPose[bone] * Parent.WorldTransforms[bone];
            }
        }
    }
}
